//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Media;
using OpenLS.Core.Formatting;

namespace OpenLS.Core
{
    ///<summary>
    /// Represents the format of a value. 
    ///</summary>
    public abstract class Format
    {
        protected Format(string code)
        {
            _code = code;
            parse();
        }
        
        [Conditional ("DEBUG")]
        internal static void Warn()
        {
            // Contract.Assert(false);
            // throw new Exception();
            MessageBox.Show("Operation error");
        }
        private void parse()
        {
            var p = new FormatParser(Code);
            Analyze(p.Tokens);
        }
        ///<summary>
        /// Get the category of the format
        ///</summary>
        public abstract FormatCategory Category { get;}

        internal abstract void Analyze(List<FormatToken> list);
        ///<summary>
        /// Returns the formatted value of an object. In most cases, the returned value is a string.
        ///</summary>
        ///<param name="obj"></param>
        ///<returns></returns>
        public object GetFormattedValue(object obj)
        {
            Contract.Assert(obj == null || (obj.GetType().Name != "RichTextParagraph"));
            return GetFormattedValue(obj, null);
        }
        ///<summary>
        /// Returns the formatted value of an object. In most cases, the returned value is a string.
        ///</summary>
        ///<param name="obj"></param>
        ///<param name="converter"></param>
        ///<returns></returns>
        public abstract object GetFormattedValue(object obj, IFormatValueConverter converter);
        ///<summary>
        /// Indicates whether an object can be formatted by the current format
        ///</summary>
        ///<param name="obj"></param>
        ///<returns></returns>
        public abstract bool CanFormat(object obj);
        
        ///<summary>
        /// Converts a string to a format.
        ///</summary>
        ///<param name="formatCode">A string containing the code of the format to convert</param>
        ///<returns></returns>
        ///<exception cref="ArgumentNullException"></exception>
        ///<exception cref="NotSupportedException"></exception>
        public static Format Parse(string formatCode)
        {
            if (formatCode == null)
                throw new ArgumentNullException("formatCode");
            Format result;
            if (_table == null)
                _table = new Dictionary<string, Format>();
            if (_table.TryGetValue(formatCode, out result))
                return result;
            var p = new FormatParser(formatCode);
            switch (p.GetCategory())
            {
                case FormatCategory.General:
                    result = Formats.General; break;
                case FormatCategory.Number:
                    result = new NumberFormat(formatCode); break;
                case FormatCategory.Currency:
                    result = new CurrencyFormat(formatCode); break;
                case FormatCategory.Percent:
                    result = new PercentFormat(formatCode); break;
                case FormatCategory.Date:
                    result = new DateTimeFormat(formatCode); break;
                case FormatCategory.Text:
                    result = new TextFormat(formatCode); break;
                case FormatCategory.Fraction:
                    result = new FractionFormat(formatCode); break;
                case FormatCategory.Scientific:
                    result = new ScientificFormat(formatCode); break;
                default: throw new NotSupportedException();
            }
            _table.Add(formatCode, result);
            return result;
        }

        [ThreadStatic]
        static Dictionary<string, Format> _table ;
        private readonly string _code;
        ///<summary>
        /// Get the code of the format. The code is a string representation of the format used to serialize to file.
        ///</summary>
        public string Code
        {
            get { return _code; }
        }
        public sealed override int GetHashCode()
        {
            return Code.GetHashCode();
        }
        public sealed override bool Equals(object obj)
        {
            var other = obj as Format;
            if (other == null)
                return false;
            return Code == other.Code;
        }
        private readonly List<FormatClause> _clauses = new List<FormatClause>();

        internal List<FormatClause> Clauses
        {
            get { return _clauses; }
        }
        
        ///<summary>
        /// Returns the codor used to display a value. If no
        ///</summary>
        ///<param name="value"></param>
        ///<returns></returns>
        public Color? GetColor(object value)
        {
            if (value is double)
            {
                double d = (double)value;
                foreach (FormatClause e in Clauses)
                    if (e.Matches(d))
                        return e.color;
            }
            return null;
        }

        internal static object BooleanToString(bool p)
        {
            if (p)
                return "TRUE";
            return "FALSE";
        }

        /// <summary>
        /// SJE 22-07-2013 comparison operators between formats
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator < (Format a, Format b)
        {
            if (b is GeneralFormat)
                return true;

            if (b is TextFormat)
            {
                if (a is DateTimeFormat)
                    return true;
                if (a is NumberFormat)
                    return true;
                if (a is FractionFormat)
                    return true;
            }
            if (b is NumberFormat)
            {
                if (a is FractionFormat)
                    return true;
            }
            return false;
        }


        /// <summary>
        /// SJE 22-07-2013 comparison operators between formats
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator > (Format a, Format b)
        {
            return !(b < a);
        }

       
    }
}
